#include "spi.h"
#include "io.h"
#include "../lib/inc/stm32f10x_conf.h"

typedef struct {
    uint8_t nss;  // cs
    uint8_t sck;
    uint8_t miso;
    uint8_t mosi;
} SPI_PINS_T;

// SPI   NSS   SCK   MISO  MOSI
// SPI1  PA4   PA5   PA6   PA7
//       PA15  PB3   PB4   PB5 - remap
// SPI2  PB12  PB13  PB14  PB15
// SPI3  PA15  PB3   PB4   PB5
//       PA4   PC10  PC11  PC12 - remap
const SPI_PINS_T spi_pins[][2] = {
    {{PA4, PA5, PA6, PA7}, {PA15, PB3, PB4, PB5}},
    {{PB12,PB13, PB14, PB15}, {PB12,PB13, PB14, PB15}},
    {{PA15, PB3, PB4, PB5}, {PA4, PC10, PC11, PC12}},
};

const SPI_TypeDef * spi_devs[] = {
    SPI1,
    SPI2,
    SPI3,
};


void SpiGpioInit(SPI_DEV_E spi, uint8_t is_remap) {
    SPI_PINS_T pin = is_remap ? spi_pins[spi][1] : spi_pins[spi][0];
    IODisableJtag();
    IOInit(pin.nss, Mode_AF_PP);
    IOInit(pin.sck, Mode_AF_PP);
    IOInit(pin.miso, Mode_IN_FLOATING);
    IOInit(pin.mosi, Mode_AF_PP);
    IOWrite(pin.nss,  1);
    IOWrite(pin.sck,  1);
    IOWrite(pin.miso, 1);
    IOWrite(pin.mosi, 1);
    if (!is_remap) 
        return;
    switch (spi) {
        case SPI_1:
            GPIO_PinRemapConfig(GPIO_Remap_SPI1, ENABLE);
            break;
        case SPI_2:
            break;  // spi2 no remap
        case SPI_3:
            GPIO_PinRemapConfig(GPIO_Remap_SPI3, ENABLE);
            break;
    }
}

void SpiInit(SPI_DEV_E spi, uint8_t is_remap) {
    SPI_InitTypeDef SPI_InitStructure;
    SpiGpioInit(spi, is_remap);
    SPI_Cmd((SPI_TypeDef *)spi_devs[spi], DISABLE);
    RCC_APB2PeriphClockCmd(	RCC_APB2Periph_AFIO,  ENABLE );
    switch (spi) {
        case SPI_1:
            RCC_APB2PeriphClockCmd(	RCC_APB2Periph_SPI1,  ENABLE );
            break;
        case SPI_2:
            RCC_APB1PeriphClockCmd(	RCC_APB1Periph_SPI2,  ENABLE );
            break;
        case SPI_3:
            RCC_APB1PeriphClockCmd(	RCC_APB1Periph_SPI3,  ENABLE );
            break;
    }
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线双向全双工，还有半双工以及串行发和串行收方式
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主 SPI   还有副 SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收8或者16位帧结构
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步时钟的空闲状态为高电平或者低电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第一个或者第二个跳变沿数据被采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; //NSS 信号由软件控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //预分频 256
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
    SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
    SPI_Init((SPI_TypeDef *)spi_devs[spi], &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器
    SPI_Cmd((SPI_TypeDef *)spi_devs[spi], ENABLE); //使能SPI外设
    SPI_SSOutputCmd((SPI_TypeDef *)spi_devs[spi], ENABLE);
    SPI_I2S_SendData((SPI_TypeDef *)spi_devs[spi], 0xff);
}

//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
uint8_t SPI_ReadWriteByte(SPI_DEV_E spi, uint8_t tx_data) {		
	uint8_t retry=0;				 	
	SPI_I2S_SendData((SPI_TypeDef *)spi_devs[spi], tx_data); //通过外设SPIx发送一个数据
	while (SPI_I2S_GetFlagStatus((SPI_TypeDef *)spi_devs[spi], SPI_I2S_FLAG_TXE) == RESET) { //检查指定的SPI标志位设置与否:发送缓存空标志位
		retry++;
		if(retry>200)
            return 0;
    }
	retry=0;

	while (SPI_I2S_GetFlagStatus((SPI_TypeDef *)spi_devs[spi], SPI_I2S_FLAG_RXNE) == RESET) { //检查指定的SPI标志位设置与否:接受缓存非空标志位
		retry++;
		if(retry>200)
            return 0;
	}	  						    
	return SPI_I2S_ReceiveData((SPI_TypeDef *)spi_devs[spi]); //返回通过SPIx最近接收的数据					    
}